package com.dny.asmtop.op;

import com.dny.asmtop.ASMMethodUtils;
import com.dny.asmtop.CB;
import com.dny.asmtop.Command;
import com.dny.asmtop.MethodContext;
import jdk.internal.org.objectweb.asm.Label;
import jdk.internal.org.objectweb.asm.Type;
import jdk.internal.org.objectweb.asm.commons.GeneratorAdapter;

import java.util.Iterator;
import java.util.Map;
import java.util.Objects;

/**
 * Created by jlutt on 2018-01-17.
 * Map foreach操作
 * @author jlutt
 */
public class CommandMapForEach implements Command {

  /**
   * map对象
   */
  private final Command map;

  /**
   * key循环操作
   */
  private final ForVar forKey;

  /**
   * value循环操作
   */
  private final ForVar forValue;

  public CommandMapForEach(Command map, ForVar forKey, ForVar forValue) {
    this.map = map;
    this.forKey = forKey;
    this.forValue = forValue;
  }

  @Override
  public Type type(MethodContext context) {
    return Type.VOID_TYPE;
  }

  @Override
  public Type generator(MethodContext context) {
    GeneratorAdapter g = context.getGeneratorAdapter();

    Label labelLoop = new Label();
    Label labelExit = new Label();

    //获取map操作迭代器
    CB.call(CB.call(map, "entrySet"), "iterator").generator(context);
    VarLocal varIter = ASMMethodUtils.newLocal(context, Type.getType(Iterator.class));
    varIter.store(context);

    g.mark(labelLoop);

    CB.call(varIter, "hasNext").generator(context);
    g.push(false);
    g.ifCmp(Type.BOOLEAN_TYPE, GeneratorAdapter.EQ, labelExit);

    CB.cast(CB.call(varIter, "next"), Map.Entry.class).generator(context);
    VarLocal varEntry = ASMMethodUtils.newLocal(context, Type.getType(Map.Entry.class));

    varEntry.store(context);

    forKey.forLoop(CB.call(varEntry, "getKey")).generator(context);
    forValue.forLoop(CB.call(varEntry, "getValue")).generator(context);

    g.goTo(labelLoop);
    g.mark(labelExit);
    return Type.VOID_TYPE;
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    CommandMapForEach that = (CommandMapForEach) o;
    return Objects.equals(map, that.map);
  }

  @Override
  public int hashCode() {

    return Objects.hash(map);
  }
}
